package club.kynb.mall.application.controller;

import club.kynb.mall.application.config.OssConfig;
import club.kynb.mall.application.model.model.command.CaptchaSaveCommand;
import club.kynb.mall.application.model.model.command.SmsSendCommand;
import club.kynb.mall.application.model.model.dto.AppInfoDTO;
import club.kynb.mall.application.model.model.dto.FileDTO;
import club.kynb.mall.application.service.GlobalApplicationService;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.ShearCaptcha;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.aliyun.oss.model.PutObjectResult;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.pizza.common.web.exception.Errors;
import org.pizza.common.web.response.R;
import org.pizza.util.Checker;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author kynb_club@163.com
 * @since 2021/6/24 3:34 下午
 */
@Slf4j
@RestController
@RequestMapping
@AllArgsConstructor
@Api(value = "", tags = "全局接口")
public class GlobalController {
    private final GlobalApplicationService globalApplicationService;
    private final OssConfig ossConfig;
    private final OSS oss;

    @PostMapping("/global/sendSms")
    @ApiOperation(value = "全局-发送短信验证码", notes = "")
    public R<Void> sendSms(@Valid @RequestBody SmsSendCommand command) {
        globalApplicationService.smsSend(command);
        return R.ok();
    }

    @GetMapping("/global/captcha")
    @ApiOperation(value = "全局-获取图形验证码", notes = "")
    @ApiImplicitParam(name = "captchaKey", value = "验证码key",dataTypeClass = String.class, dataType = "String", example = "1234-1234-1234-1234", required = true)
    public void captcha(HttpServletResponse response, @RequestParam(defaultValue = "1234-1234-1234-1234") String captchaKey) {
        //定义图形验证码的长、宽、验证码字符数、干扰线宽度
        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(200, 100, 4, 4);
        String code = captcha.getCode();
        CaptchaSaveCommand captchaSaveCommand = new CaptchaSaveCommand();
        captchaSaveCommand.setCaptchaKey(captchaKey);
        if (captchaKey.equals("1234-1234-1234-1234")) {
            captchaSaveCommand.setCaptchaCode("1234");
        } else {
            captchaSaveCommand.setCaptchaCode(code);
        }
        globalApplicationService.captchaSave(captchaSaveCommand);
        try {
            //设置相应类型,告诉浏览器输出的内容为图片
            response.setContentType("image/jpeg");
            //设置响应头信息，告诉浏览器不要缓存此内容
            response.setHeader("Pragma", "No-cache");
            response.setHeader("Cache-Control", "no-cache");
            captcha.write(response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @PostMapping("/global/file/upload")
    @ApiOperation(value = "全局-文件上传接口", notes = "传入MultipartFile")
    public R<FileDTO> upload(@RequestParam("file") MultipartFile file) {
        Checker.ifNullThrow(file, (o) -> log.error("上传文件MultipartFile不能为空"), () -> Errors.BIZ.exception("上传文件MultipartFile不能为空"));
        try {
            String originalFilename = file.getOriginalFilename();
            Checker.ifNullThrow(originalFilename, (o) -> log.error("上传文件无法解析文件名"), () -> Errors.BIZ.exception("上传文件无法解析文件名"));
            String[] filename = originalFilename.split("\\.");
            String suffix = filename[filename.length - 1];
            //生成新文件名
            String id = RandomUtil.randomString(24);
            String newFileName = id + "." + suffix;
            PutObjectResult result = oss.putObject(ossConfig.getBucket(), newFileName, file.getInputStream());
            log.info("OSS上传文件: {} 结果:{}", newFileName, result);
            //
            Date expiration = new Date(System.currentTimeMillis() + 300 * 1000);
            URL url = oss.generatePresignedUrl(ossConfig.getBucket(), newFileName, expiration);
            FileDTO fileDTO = new FileDTO();
            fileDTO.setObjectName(newFileName);
            fileDTO.setPreviewUrl(url.toString());
            fileDTO.setExpireAt(expiration.getTime());
            return R.ok(fileDTO);
        } catch (IOException e) {
            log.error("OSS 文件上传失败", e);
            throw Errors.SYSTEM.exception("文件上传失败");
        }
    }

    @GetMapping("/global/file/getUrl")
    @ApiOperation(value = "全局-生成文件临时访问地址接口", notes = "传入objectName、expire（单位秒）")
    public R<FileDTO> getUrl(String objectName, String originalFileName, Integer expire) {
        Date expiration = new Date(System.currentTimeMillis() + expire * 1000);
        URL url = oss.generatePresignedUrl(ossConfig.getBucket(), objectName, expiration);
        FileDTO fileDTO = new FileDTO();
        fileDTO.setObjectName(objectName);
        fileDTO.setObjectName(originalFileName);
        fileDTO.setPreviewUrl(url.toString());
        fileDTO.setExpireAt(expiration.getTime());
        return R.ok(fileDTO);
    }

    @GetMapping("/global/file/generate")
    @ApiOperation(value = "全局-获取客户端上传凭证信息接口", notes = "")
    public R<Map<String, String>> generate() {
        long expireTime = 300;
        long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
        Date expiration = new Date(expireEndTime);
        // PostObject请求最大可支持的文件大小为5 GB，即CONTENT_LENGTH_RANGE为5*1024*1024*1024。
        PolicyConditions policyConditions = new PolicyConditions();
        policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 50 * 1024 * 1024);
        policyConditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, "file/");
        String postPolicy = oss.generatePostPolicy(expiration, policyConditions);
        byte[] binaryData;
        binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
        String encodedPolicy = BinaryUtil.toBase64String(binaryData);
        String postSignature = oss.calculatePostSignature(postPolicy);
        // host的格式为 bucket.endpoint
        String host = "https://" + ossConfig.getBucket() + "." + ossConfig.getEndpoint();
        Map<String, String> respMap = new LinkedHashMap<>();
        respMap.put("accessKey", ossConfig.getAccessKey());
        respMap.put("policy", encodedPolicy);
        respMap.put("signature", postSignature);
        respMap.put("dir", "file/");
        respMap.put("host", host);
        respMap.put("expire", String.valueOf(expireEndTime / 1000));
        return R.ok(respMap);
    }

    @GetMapping("/global/file/sts/generate")
    @ApiOperation(value = "全局-获取客户端STS上传凭证信息接口", notes = "")
    public R<Map<String, String>> stsGenerate(@RequestParam(value = "userId",required = false)String userId) {
        try {
            // 构造client。
            IClientProfile profile = DefaultProfile.getProfile(ossConfig.getRegionId(), ossConfig.getAccessKey(), ossConfig.getAccessSecret());
            DefaultAcsClient client = new DefaultAcsClient(profile);
            final AssumeRoleRequest request = new AssumeRoleRequest();
            request.setSysMethod(MethodType.POST);
            request.setSysRegionId(ossConfig.getRegionId());
            request.setRoleArn(ossConfig.getRoleArn());
            request.setRoleSessionName(ossConfig.getRoleSessionNamePrefix() + StrUtil.emptyToDefault(userId,"default"));
            request.setDurationSeconds(ossConfig.getExpireTime()); // 设置临时访问凭证的有效时间。
            final AssumeRoleResponse response = client.getAcsResponse(request);
            Map<String, String> respMap = new LinkedHashMap<>();
            respMap.put("accessKeyId",response.getCredentials().getAccessKeyId());
            respMap.put("accessKeySecret",response.getCredentials().getAccessKeySecret());
            respMap.put("stsToken",response.getCredentials().getSecurityToken());
            respMap.put("requestId",response.getRequestId());
            respMap.put("expire",response.getCredentials().getExpiration());
            respMap.put("bucket",ossConfig.getBucket());
            respMap.put("regionId",ossConfig.getRegionId());
            respMap.put("endpoint",ossConfig.getEndpoint());
            respMap.put("host",ossConfig.getHost());
            return R.ok(respMap);
        } catch (ClientException e) {
            log.error("OSS sts 凭证获取失败",e);
            throw Errors.BIZ.exception("上传文件凭证获取失败");
        }
    }


    @GetMapping("/global/appInfo")
    @ApiOperation(value = "全局-获取应用信息接口", notes = "")
    public R<AppInfoDTO> getAppInfo(){
        return R.ok(globalApplicationService.getAppInfo());
    }




}
